﻿using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using UnityEditorInternal;
using UnityEditor.SceneManagement;
using UnityEngine.Rendering;
using System.Reflection;
using System;
using System.IO;
using System.Linq;

namespace Nirvana
{
    public class ShaderVariantsCollectEditor : EditorWindow
    {
        static int MinWidth = 475;
        string searchPath = "Assets/Resources/FruitRogue/Art";
        string tmpShaderVeriantPath;
        //需要打包变体的shader
        List<string> needFilterShaders = new List<string>()
        {
            "URP/GameParticlesPlus",
            "URP/GameEnemyPlus",
            "URP/GameParticlesPlusUI",
        };
        string tempFilterShaders = "";


        string jumpPage = "1";
        int pageNum = 1;
        int row = 15;                   //行
        List<ShaderVariantCollection> dirList = new List<ShaderVariantCollection>();
        Vector2 scrollPos = new Vector2(0, 0);
        ShaderVariantCollection hightLightTarget;

        [MenuItem("Nirvana/Shader变体收集")]
        public static void OpenMonoBehaviour()
        {
            ShaderVariantsCollectEditor window = (ShaderVariantsCollectEditor)EditorWindow.GetWindow(typeof(ShaderVariantsCollectEditor));
            window.Show();
            window.minSize = new Vector2(MinWidth, 475);
        }

        private void OnGUI()
        {
            GUILayout.Space(10f);

            GUILayout.Label("Shader变体收集说明", GUITheme.LabelTitleStyle);
            EditorGUILayout.HelpBox("Shader变体收集器，需要和Shader存放一起，以便AB能够加载到对应的Shader功能", MessageType.None);
            EditorGUILayout.Space();

            GUILayout.Space(20);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel("查找路径:");
            searchPath = EditorGUILayout.TextArea(searchPath);
            EditorGUILayout.EndHorizontal();
            GUILayout.Space(20);

            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel("需要分配变体的Shader:");
            GUI.enabled = false;
            tempFilterShaders = "";
            for (int i = 0; i < needFilterShaders.Count; i++)
            {
                tempFilterShaders += needFilterShaders[i] + "\n";
            }
            EditorGUILayout.TextArea(tempFilterShaders);
            EditorGUILayout.EndHorizontal();
            GUI.enabled = true;
            GUILayout.Space(20);


            if (GUILayout.Button("1.首先:清除环境内Shader变体缓存"))
            {
                ClearAllShaderVariants();
            }

            if (GUILayout.Button("2.收集所有Shader变体"))
            {
                StartShaderVariantsCollect();
            }

            if (GUILayout.Button("3.保存Shader变体"))
            {
                SaveShaderVariants();
            }

            if (GUILayout.Button("4.过滤需要的Shader变体文件"))
            {
                FilterShaderFile();
            }

            if (GUILayout.Button("5.显示Shader变体文件"))
            {
                AddNewShowShaderVeriant();
            }
            EditorGUILayout.Space();

            ShowNewShaderVeriant();
        }

        private void StartShaderVariantsCollect()
        {
            EditorApplication.EnterPlaymode();

            var scene = EditorSceneManager.NewScene(NewSceneSetup.DefaultGameObjects, NewSceneMode.Single);
            Camera.main.fieldOfView = 60;
            Camera.main.transform.position = new Vector3(0, 150, -280);
            Camera.main.transform.rotation = Quaternion.Euler(35, 0, 0);
            CheckPrefabForAssets();

        }

        private void CheckPrefabForAssets()
        {
            var assets = AssetDatabase.FindAssets("t:prefab", new string[] { searchPath });
            int startIndex = 0;
            int maxLength = assets.Length;
            using (ProgressIndicator progress = new ProgressIndicator("generate asset keys", maxLength))
            {
                for (int i = startIndex; i < maxLength; i++)
                {
                    var guid = assets[i];
                    var path = AssetDatabase.GUIDToAssetPath(guid);

                    progress.SetProgress(i);
                    progress.ShowForce("check asset:{0}", path);

                    var prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
                    if (prefab != null)
                    {
                        var go = Instantiate<GameObject>(prefab);
                        go.SetActive(true);
                        ChangeChildLayer(go.transform, LayerMask.NameToLayer("Default"));
                    }
                }
            }
        }
        /// <summary>
        /// 保存Shader变体
        /// </summary>
        private void SaveShaderVariants()
        {
            string message = "Save shader variant collection";
            string text = EditorUtility.SaveFilePanelInProject("Save Shader Variant Collection", "NewTempShaderVariants", "shadervariants", message);
            if (!string.IsNullOrEmpty(text))
            {
                tmpShaderVeriantPath = text;
                //ShaderUtil.SaveCurrentShaderVariantCollection(text);

                Assembly assembly = Assembly.GetAssembly(typeof(EditorWindow));
                Type type = assembly.GetType("UnityEditor.ShaderUtil");
                MethodInfo method = type.GetMethod("SaveCurrentShaderVariantCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
                method?.Invoke(null, new object[] { text });
                AssetDatabase.Refresh();
            }

        }

        /// <summary>
        /// 清除Shader变体缓存数据
        /// </summary>
        private void ClearAllShaderVariants()
        {
            //ShaderUtil.ClearCurrentShaderVariantCollection();

            //反射变体清除接口
            Assembly assembly = Assembly.GetAssembly(typeof(EditorWindow));
            Type type = assembly.GetType("UnityEditor.ShaderUtil");
            MethodInfo method = type.GetMethod("ClearCurrentShaderVariantCollection", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
            method?.Invoke(null, null);

        }

        private void FilterShaderFile()
        {
            if (string.IsNullOrEmpty(tmpShaderVeriantPath))
                return;

            var veria = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(tmpShaderVeriantPath);
            if (veria != null)
            {

                SerializedObject serializedObject = new UnityEditor.SerializedObject(veria);
                SerializedProperty m_Shaders = serializedObject.FindProperty("m_Shaders");
                for (int i = 0; i < m_Shaders.arraySize; ++i)
                {
                    SerializedProperty pair = m_Shaders.GetArrayElementAtIndex(i);

                    SerializedProperty first = pair.FindPropertyRelative("first");
                    SerializedProperty second = pair.FindPropertyRelative("second");//ShaderInfo

                    Shader shader = first.objectReferenceValue as Shader;

                    if (shader == null)
                        continue;
                    if (!needFilterShaders.Contains(shader.name)) continue;

                    ShaderVariantCollection newShaderVariant = new ShaderVariantCollection();

                    SerializedProperty variants = second.FindPropertyRelative("variants");
                    for (var vi = 0; vi < variants.arraySize; ++vi)
                    {
                        SerializedProperty variantInfo = variants.GetArrayElementAtIndex(vi);

                        ShaderVariantCollection.ShaderVariant variant = PropToVariantObject(shader, variantInfo);
                        newShaderVariant.Add(variant);
                    }

                    var shaderFilepath = AssetDatabase.GetAssetPath(Shader.Find(shader.name));
                    var shaderDir = Path.GetDirectoryName(shaderFilepath);
                    var newShaderVeriantPaht = string.Format("{0}/{1}Variants.shadervariants", shaderDir, Path.GetFileName(shader.name));
                    Debug.Log("Create New Shader Veraint Path: " + newShaderVeriantPaht);
                    AssetDatabase.CreateAsset(newShaderVariant, newShaderVeriantPaht);

                }

                AssetDatabase.Refresh();
            }

            AddNewShowShaderVeriant();
            Debug.Log("分配各种Shader变体完成!");
        }

        private void AddNewShowShaderVeriant()
        {
            dirList.Clear();
            var assets = (from s in AssetDatabase.GetAllAssetPaths() where s.EndsWith(".shadervariants") select s).ToArray();
            for (int i = 0; i < assets.Length; ++i)
            {
                Debug.Log(assets[i]);
                var veria = AssetDatabase.LoadAssetAtPath<ShaderVariantCollection>(assets[i]);
                dirList.Add(veria);
            }
        }

        private void ShowNewShaderVeriant()
        {

            GUI.color = Color.green;
            if (dirList != null && dirList.Count > 0)
            {
                scrollPos = EditorGUILayout.BeginScrollView(scrollPos);
                foreach (var dic in dirList)
                {
                    string str = dic.name;
                    var editorStyle = EditorStyles.helpBox;
                    if (hightLightTarget != null && hightLightTarget == dic)
                    {
                        editorStyle = EditorStyles.objectFieldThumb;
                        //str = str + "    (当前选择!!!!!)";
                    }

                    if (GUILayout.Button(str, editorStyle, GUILayout.Height(20f)))
                    {
                        hightLightTarget = dic;
                        //根据Object指引对应的位置
                        EditorGUIUtility.PingObject(hightLightTarget);
                        Selection.activeObject = hightLightTarget;
                    }
                }

                EditorGUILayout.EndScrollView();
            }
            GUI.color = Color.white;
        }


        //将SerializedProperty转化为ShaderVariant
        private ShaderVariantCollection.ShaderVariant PropToVariantObject(Shader shader, SerializedProperty variantInfo)
        {
            PassType passType = (PassType)variantInfo.FindPropertyRelative("passType").intValue;
            string keywords = variantInfo.FindPropertyRelative("keywords").stringValue;
            string[] keywordSet = keywords.Split(' ');
            keywordSet = (keywordSet.Length == 1 && keywordSet[0] == "") ? new string[0] : keywordSet;

            ShaderVariantCollection.ShaderVariant newVariant = new ShaderVariantCollection.ShaderVariant()
            {
                shader = shader,
                keywords = keywordSet,
                passType = passType
            };

            return newVariant;
        }



        private void ChangeChildLayer(Transform t, int layer)
        {
            t.gameObject.layer = layer;
            for (int i = 0; i < t.childCount; i++)
            {
                Transform child = t.GetChild(i);
                child.gameObject.layer = layer;
                child.gameObject.SetActive(true);
                ChangeChildLayer(child, layer);
            }
        }
    }

}